#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/fcntl.h>

#include <event2/event.h>
struct event* connev = NULL;

// 初始化服务端的监听端口。
int initserver(int port);

//回调函数
//typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void* arg);
void readcb(evutil_socket_t fd, short events, void* arg)
{
    int n = 0;
    char buf[1024];

    memset(buf,0,sizeof(buf));
    n = read(fd,buf,sizeof(buf));
    if(n <= 0)
    {
        close(fd);
        //将通信文件描述符从对应时间从base地基上删除
        event_del(connev);
        return;
    }
    //read(fd,buf,n);
    write(fd,buf,n);
}

//typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void* arg);
void conncb(evutil_socket_t fd, short events, void* arg)
{
    struct event_base* base = (struct event_base*)arg;
    int cfd = accept(fd,NULL,NULL);
    if(fd > 0)
    {
        //创建通信文件描述符对应事件并设置回调函数readcb
        connev = event_new(base,cfd, EV_READ|EV_PERSIST,readcb,NULL);
        if(connev == NULL)
        {
            //退出循环
            event_base_loopexit(base,NULL);
        }
        //将通信文件描述符是对应事件上event_base地基
        event_add(connev,NULL);

    }
}


int main(int argc,char *argv[])
{
    if (argc != 2)
    {
    printf("usage: ./tcpselect port\n"); return -1;
    }

    // 初始化服务端用于监听的socket。
    int lfd = initserver(atoi(argv[1]));
    printf("lfd=%d\n",lfd);

    if (lfd < 0)
    {
    printf("initserver() failed.\n"); return -1;
    }

    //创建地基
    struct event_base* base = event_base_new();
    if(base == NULL)
    {
        return -1;
    }

    //创建文件描述符对应事件
    //typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void* arg);
    struct event* ev = event_new(base,lfd, EV_READ|EV_PERSIST, conncb,base);
    if(ev == NULL)
    {
        return -1;
    }
    //将新的事件节点添加到base地基上
    event_add(ev,NULL);
    //进入事件循环
    event_base_dispatch(base);
    //释放资源
    event_base_free(base);
    event_free(ev);

    close(lfd);

    return 0;
}

// 初始化服务端的监听端口。
int initserver(int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if (sock < 0)
    {
    printf("socket() failed.\n"); return -1;
    }

    // Linux如下 解决断开连接后 短时间内再次连接同一端口 出现端口占用的情况
    int opt = 1; unsigned int len = sizeof(opt);
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,len);
    setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,&opt,len);

    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);

    if (bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0 )
    {
    printf("bind() failed.\n"); close(sock); return -1;
    }

    if (listen(sock,5) != 0 )
    {
    printf("listen() failed.\n"); close(sock); return -1;
    }

    return sock;
}